package org.biloxi.card;

import java.util.Set;

import org.biloxi.builders.subjects.cardsubjects.HaveCardSubject;
import org.biloxi.model.GameState;
import org.biloxi.model.GameState.Phase;
import org.biloxi.model.card.CardInstance;
import org.biloxi.player.PlayerID;

/**
 * This is the specification of a MTG Card.
 * This class separates the specification (or declaration) from an instance of a card.
 * 
 * Card developers will most likely subclass implementations of this class (AbstractCard).  
 * 
 * @author Peter.WM.Wong@gmail.com
 */
public abstract class Card {
	public static enum Type{Spell, Creature, Instant, Enchantment, Artifact, Land};
	
	/**
	 * Unique Name of the MTG Card.
	 * This is usually at the top-left of the card.
	 * 
	 * @return Name of the card.
	 */
	public abstract String getName();
	
	/**
	 * Part of the text description that specifies any game rules.
	 *  
	 * @return Game rule description of the card. 
	 */
	public abstract String getDescription();

	/**
	 * Part of the text description that does not specify any game rules.
	 *  
	 * @return Non Game rule text on the card.  
	 */
	public abstract String getExtraText();
	
	/**
	 * The type of the card. {@link Type}
	 * 
	 * @return Type of the card.
	 */
	public abstract Type getType();
	
	/**
	 * Specifies a list of subtypes the card can be.
	 * Here are some examples:
	 * 
	 * "Skyshroud Sentinel" 
	 * 		Type: Creature 
	 * 		Subtype(s): "Elf"
	 * 
	 * "Jolrael's Centaur" 
	 * 		Type: Creature 
	 * 		Subtype(s): "Centaur"
	 * 
	 * "Cat Warriors" 
	 * 		Type: Creature 
	 * 		Subtype(s): "Cat", "Warrior"
	 * 
	 * @return String array of subtypes
	 */
	public abstract String[] getSubTypes(); 

	
	protected final HaveCardSubject when(CardInstance c){
		return _g.getCardSubject(c);
	}
	
	/**
	 * Called when an instance of this Card is played.
	 * Specifically, when the CardInstance is put on the stack during a players priority.
	 * 
	 * @param cardInst
	 */
	public abstract void whenPlayed(final CardInstance cardInst);
	
	/**
	 * Called when an instance of this Card is being resolved from the stack.
	 * 
	 * TODO Land cards are a special case, where whenPlayed and whenResolved are the same...
	 * TODO LandCard subclass Card, and locks out whenResolved...
	 * TODO class LandCard extends Card { public final void whenResolved(...){} } 
	 * 
	 * @param cardInst
	 */
	public abstract void whenResolved(final CardInstance cardInst);
	
	/**
	 * Called when a card is moved to the Graveyard.
	 * 
	 * TODO Discarded always means Graveyard? What about taken-out-the-game?
	 * 
	 * @param cardInst
	 */
	public abstract void whenDiscarded(final CardInstance cardInst);
	
	/** 
	 * Called when a card is taken from the Library.
	 * 
	 * @param cardInst
	 */
	public abstract void whenDrawn(final CardInstance cardInst);
	
	
	/**
	 * Gets the player id of whoever's turn it is.
	 * @return
	 */
	protected final PlayerID turnsPlayer(){
		return _g.getTurnsPlayerID();
	}
		
	/**
	 * Gets the phase of the game.
	 * @return
	 */
	protected final Phase currentPhase(){
		return _g.getPhase();
	}
	
	/**
	 * @return number of players
	 */
	protected final int numPlayers(){
		return _g.numPlayers();
	}
		
	/**
	 * Retrieves the list PlayerIDs that are opponents to the given PlayerID.
	 *  
	 * @param pid
	 * @return
	 */
	protected final Set<PlayerID> oppenentsOf(PlayerID pid){
		return _g.getOppenents(pid);
	}

	// Nasty little hack for the greater good...
	// We'll use reflection to call setGame and inject a reference to the GameState.
	// This benefits card developers in the following ways:
	//    1) Don't have to define a boiler plate Constructor
	//    2) Easy access to convenience methods (numPlayers(), opponentsOf(pid), etc.)
	private GameState _g=null;
	private final void setGame(GameState g){_g = g;}
}
